package main.constraint;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;
import automata.Alphabet;
import automata.Fsa;
import automata.Label;
import automata.State;
import automata.StateSet;
import automata.Transitions;

/**
 * XML Functional Dependence
 * ---------------------------
 * 
 * This class represents a functional dependence
 * XML with the transformation of path expressions in
 * Finite automata.
 * Thus we have:
 *
 * xdf = (M, (T'-> T"))
 *
 * where:
 * M and the context path;
 * T'and the source path or left side;
 * T"and the destination path or right side;
 * 
 * @author Eder P. C. Flor
 */

//@SuppressWarnings("unchecked")
public class XDF extends ConstraintAbstract
{
	
	public XDF(String name, Fsa f1, Fsa f2, Fsa f3)
	{
		nameConstraint = name;
		contextFsa = f1;
		sourceFsa = f2;
		targetFsa = f3;
	}

	public XDF() {}

	/**
	 * Function that creates a path from the context FSA
	 * @param s
	 * @return void, but set new context
	 */	
	public void setContextFsa(String s)
	{
		contextFsa = pathToFsa(s);
	}

	/**
	 * Function that creates a path from the source FSA
	 * And there you have the token |
	 * @param s
	 * @return void, but set new source
	 */
	public void setSourceFsa(String s)
	{
		sourceFsa = pathToFsa(s);
		StringTokenizer st = new StringTokenizer(s, " ");
		String token = new String();
		
		while (st.hasMoreTokens())
		{
			token = st.nextToken();
			if (token.equals("|"))
			{
				npaths++;
			}
		}
		setJ();
	}

	/**
	 * Function that creates a path from the targetFsta
	 * @param s
	 * @return void, but set new targetFsa
	 */
	public void setTargetFsa(String s)
	{
		targetFsa = pathToFsa(s);
	}

	/**
	 * Function that creates FSA (automaton) from a path
	 * @param path
	 * @return new FSA
	 */
	public Fsa pathToFsa(String path)
	{
		//System.out.println("Path To FSA: " + path);
		Fsa f = new Fsa();
		int i = 1;

		Label star = new Label("*");
		State start = new State(1);
		State sant;
		State s;
		Alphabet alpha = new Alphabet();
		StateSet ss = new StateSet();
		StateSet fs = new StateSet();
		Transitions t = new Transitions();
		String token = new String();

		ss.add(start);
		sant = start;
		StringTokenizer st = new StringTokenizer(path, " ");
		
		while (st.hasMoreTokens())
		{
			token = st.nextToken();
			if (token.equals("|"))
			{
				fs.add(sant);
				sant = start;
			}

			if (token.equals("//"))
			{
				alpha.add(star);
				StateSet ss2 = new StateSet();
				ss2.add(sant);
				t.put(sant, star, ss2);
			}

			if ((!token.equals("/")) && (!token.equals("//")) && (!token.equals("|")))
			{
				Label l = new Label(token);
				if(alpha.contains(l))
				{
					sant = new State (sant.elt+1);
				}
				else
				{
					i++;
					alpha.add(l);
					s = new State(i);
					ss.add(s);
					StateSet ss1 = new StateSet();
					ss1.add(s);
					t.put(sant, l, ss1);
					sant = s;
				}
			}

		}
		
		fs.add(sant);
		f.set_alphabet(alpha);
		f.set_states(ss);
		f.set_start_state(start);
		f.set_final_states(fs);
		f.set_transitions(t);
		
		return f;
	}
	
	/**
	 * A patir de um FSA sabe qual tipo contextFsa, sourceFsa or targetFsa
	 * @param Object FSA
	 * @return integer 1 is contextFSA, 2 source FSA or 3 targetFSA
	 */
	public int getFsaPosition(Fsa f)
	{
		//System.out.print("Define FsaPosition ");
		if (f == contextFsa)
		{
			//System.out.println("contextFsa");
			return 1;
		}
			
		if (f == sourceFsa)
		{
			//System.out.println("sourceFsa left");
			return 2;
		}
		if (f == targetFsa)
		{
			//System.out.println("targetFsa right");
			return 3;	
		}			
		else
			return 0;
	}
	

	public int getNPaths()
	{
		return npaths;
	}

	public void setNPaths(int n)
	{
		npaths = n;
	}

	public String getEquality(Fsa automata, State node)
	{
		Hashtable<State, String> h;
		String tipo = null;
		
		if(equality.containsKey(automata))
		{
			h = equality.get(automata);
			tipo = h.get(node);
		}
		
		//Enumeration<Fsa> keysFsa = equality.keys();
		//Enumeration<State> keysState;
		/*while (keysFsa.hasMoreElements()) {
			Fsa f = keysFsa.nextElement();
			h = equality.get(f);
			System.out.println("Automata: "+f.toString());
			keysState = h.keys();
			while(keysState.hasMoreElements()){
				State s = keysState.nextElement();
				tipo = h.get(s);
				System.out.println("\nState: {"+s.elt+"}\nType of dependance: "+tipo);
			}
		}*/
		
		return tipo;
	}

	/**
	 * This function define type {V,N} and FSA (source or target)
	 * @param t
	 * @param automata left (l) or right (r)
	 * @return void
	 */
	public void setEquality(String t, String automata)
	{
		String [] types = t.split(",");
		Fsa f = null;
		
		if(automata.equals("l"))
			f = sourceFsa;
		if(automata.equals("r"))
			f = targetFsa;
		
		StateSet ss = f.get_final_states();
		int j = 0;
		Iterator<State> i = ss.iterator();
		Hashtable<State, String> h = new Hashtable<State, String>();
		
		while (i.hasNext() || j<types.length)
		{
			h.put(i.next(), types[j]);
			j++;
		}
		
		equality.put(f, h);
	}
	
	public void setJ()
	{
		Iterator<State> i = sourceFsa.get_final_states().iterator();
		
		while (i.hasNext())
		{
			j.add(i.next().elt);
		}
	}
	
	public int getJ(Integer state)
	{
		return j.indexOf(state);
	}

	public String toString() 
	{
		String str = new String();
		str = "XDF Name: " + nameConstraint + "\n";
		str += "Fsa Context: " + contextFsa.toString() + "\n";
		str += "Fsa Source: " + sourceFsa.toString() + "\n";
		str += "Fsa Target: " + targetFsa.toString() + "\n";
		str += "Paths: " + npaths;
		return str;
	}

	public String toStringWeb()
	{
		String str = new String();
		str = "XDF Name: " + nameConstraint + "<br>";
		str += "Fsa Context: " + contextFsa.toStringWeb() + "<br>";
		str += "Fsa Source: " + sourceFsa.toStringWeb() + "<br>";
		str += "Fsa Target: " + targetFsa.toStringWeb() + "<br>";
		str += "Paths: " + npaths + "<br>";
		return str;
	}
}
